#include "Components/ComponentCraftingTable.h"
#include "Terrain/Terrain.h"
#include "Managers/BlocksManager.h"
#include "Engine/MathUtils.hpp"
#include "Managers/CraftingRecipesManager.h"

#include "GameSingleton.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    uint32_t ComponentCraftingTable::getSlotCapacity(uint32_t slotIndex, int32_t cellValue)
    {
        if(slotIndex < slotsCount() - 2)
        {
            return ComponentInventoryBase::getSlotCapacity(slotIndex, cellValue);
        }
        return 0;
    }
    void ComponentCraftingTable::addSlotItems(uint32_t slotIndex, int32_t cellValue, uint32_t count)
    {
        ComponentInventoryBase::addSlotItems(slotIndex, cellValue, count);
        updateCraftingResult();
    }
    uint32_t ComponentCraftingTable::removeSlotItems(uint32_t slotIndex, uint32_t count)
    {
        uint32_t num = 0;
        if(slotIndex == resultSlotIndex())
        {
            if(matchedRecipe != nullptr)
            {
				if (matchedRecipe->remainsValue != 0 && matchedRecipe->remainsCount > 0)
				{
					if (slots[remainsSlotIndex()].count == 0 || slots[remainsSlotIndex()].value == matchedRecipe->remainsValue)
					{
						uint32_t num2 = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(matchedRecipe->remainsValue)]->getMaxStacking(matchedRecipe->remainsValue) - slots[remainsSlotIndex()].count;
						count = MathUtils::Min<uint32_t>(count, num2 / matchedRecipe->remainsCount * matchedRecipe->resultCount);
					}
					else
					{
						count = 0;
					}
				}
				count = count / matchedRecipe->resultCount * matchedRecipe->resultCount;
				num = ComponentInventoryBase::removeNetSlotItems(slotIndex, count);
				if (num > 0)
				{
					for (int32_t i = 0; i < 9; i++)
					{
                        if(matchedIngredients[i] != 0)
                        {
                            int32_t index = i % 3 + craftingGridSize * (i / 3);
							slots[index].count = MathUtils::Max<uint32_t>(slots[index].count - num / matchedRecipe->resultCount, 0);
							onSlotChange(index);
                        }
					}
					if (matchedRecipe->remainsValue != 0 && matchedRecipe->remainsCount > 0)
					{
						slots[remainsSlotIndex()].value = matchedRecipe->remainsValue;
						slots[remainsSlotIndex()].count += num / matchedRecipe->resultCount * matchedRecipe->remainsCount;
						onSlotChange(remainsSlotIndex());
					}
				}
            }
        }
        else
        {
            num = ComponentInventoryBase::removeSlotItems(slotIndex, count);
        }
        updateCraftingResult();
        return num;
    }

    void ComponentCraftingTable::updateCraftingResult()
    {
        memset(matchedIngredients, 0, sizeof(matchedIngredients));
        uint32_t num = UINT32_MAX;
		for (int32_t i = 0; i < craftingGridSize; i++)
		{
			for (int32_t j = 0; j < craftingGridSize; j++)
			{
				int32_t num2 = i + j * 3;
				int32_t slotIndex = i + j * craftingGridSize;
				int32_t slotValue = getSlotValue(slotIndex);
				uint32_t slotCount = getSlotCount(slotIndex);
				if (slotCount > 0)
				{
                    matchedIngredients[num2] = slotValue;
                    num = MathUtils::Min<uint32_t>(num, slotCount);					
				}
			}
		}
		CraftingRecipe *craftingRecipe = GameSingleton::craftingRecipesManager->findCraftingRecipe(matchedIngredients);
        if(craftingRecipe != nullptr && craftingRecipe->resultValue != 0)
        {
            matchedRecipe = craftingRecipe;
            slots[resultSlotIndex()].value = craftingRecipe->resultValue;
			slots[resultSlotIndex()].count = craftingRecipe->resultCount * num;
        }
        else
        {
            matchedRecipe = nullptr;
            slots[resultSlotIndex()].value = 0;
			slots[resultSlotIndex()].count = 0;
        }
        onSlotChange(resultSlotIndex());
    }

    void ComponentCraftingTable::load(const nlohmann::json &jsonData)
    {
        ComponentInventoryBase::load(jsonData);
        craftingGridSize = MathUtils::Sqrt<int32_t>(slotsCount() - 2);
        // updateCraftingResult();
    }

    static const std::string componentName = "CraftingTable";
    const std::string &ComponentCraftingTable::getName() const
    {
        return componentName;
    }
    const std::string &ComponentCraftingTable::GetName()
    {
        return componentName;
    }
} // namespace PocketSurvival
